/* Copyright  2009-2010
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 * @filename:  database.h
 */

#ifndef __DATABASE_H__
#define __DATABASE_H__


#include<os.h>
#include<allocator.h>
#include<debug.h>
#include<unique.h>
#include<util.h>
#include <transaction.h>

#include <hash.h>

class Bucket  ;
class UniqueID; // unique.h

class Transaction;
class Mutex ;

class DatabaseMetaData
{
    DatabaseMetaData(const DatabaseMetaData&);
    DatabaseMetaData(DatabaseMetaData&);
    DatabaseMetaData& operator =(const DatabaseMetaData& rhs) ;

public:

    DatabaseMetaData() {
      memset(dbName_,0,IDENTIFIER_LENGTH);
    }

    ulong  dbID_;

    char dbName_[IDENTIFIER_LENGTH];

    //TODO:: move this from here to system database
    //as only sys db has the limit and user
    //database does not have any limit for chunks
    uint32_t noOfChunks_;
    //maximum size of database
    ulong maxSize_;
    //current size of database
    ulong curSize_;

    //current page, this is used by the allocator to get new free page
    Page *curPage_;

    //first page, usually after this database meta data
    //getFreePage function uses this to get the free page in this database
    Page *firstPage_;


    //This mutex is taken if pageInfo is accessed or modified for
    //this database
    Mutex dbAllocMutex_;

    Mutex dbMutex_;

    Mutex dbTransTableMutex_;

    Mutex dbProcTableMutex_;
    //To generate unique id
    UniqueID chunkUniqueID_;

    //This is where all hash index nodes are stored for all the
    //indexes in this database
    Chunk *hashIndexChunk_;

    unsigned char reserved_[996];
};


class DatabaseManagerImpl;
class Table;
class ProcInfo;
class ThreadInfo;

struct tblname_ptr_struct{
    char  tblName_[IDENTIFIER_LENGTH];
    void* chunkptr  ;
    void* tblptr    ;
    size_t keylength;
};

#define BUFFERSIZE   50

class Tbl_Field{

    Tbl_Field& operator=(const Tbl_Field& );
    Tbl_Field (const Tbl_Field&);
    Tbl_Field (Tbl_Field&);
public:
    void *        tblptr   ;
    char          add_str[BUFFERSIZE];
    BackEnd::List<void> fldList  ;
    BackEnd::List<void> idxList  ;
    Tbl_Field():tblptr(0){
      memset(add_str,0,sizeof(add_str));
    }
    virtual ~Tbl_Field()
    { fldList.removeAll();
      idxList.removeAll();
    }
};
class Database
{
    Database& operator=(const Database&);
    Database(const Database&);
    Database(Database&);
private:
    //Only DatabaseManager creates this object
    //initialization is done only in DatabaseManager during
    //create, delete, open, close database methods
    Database():metaData_(NULL),
               isTblNameHashInited(false),
               isTblFieldHashInited(false),
               procSlot(-1){}

    DatabaseMetaData *metaData_;

    HASH     tablename_hash;
    HASH     tablefield_hash;
    bool     isTblNameHashInited  ;
    bool     isTblFieldHashInited ;
public:
    virtual ~Database()  ;

    int initTblNameHash() ;

    int initTblFieldHash();

    int insertTblNamePtr2Hash  (unsigned char* record)   ;

    int insertTblFieldPtr2Hash (unsigned char* record)   ;

    int removeTblNamePtrFromHash(unsigned char* key,uint32_t keylength);

    int removeTblFieldPtrFromHash(unsigned char* key,uint32_t keylength);

    struct tblname_ptr_struct* searchTblNamePtr (unsigned char* key,
                                                 uint32_t keylength);

    Tbl_Field* searchTblFieldPtr (unsigned char* key,uint32_t keylength);

    int initTransTableMutex()     ;

    BackEnd::DbRetVal createSystemDatabaseChunk
                                     (AllocType type = FixedSizeAllocator,
                                      size_t size = 0, int chunkID=-1);
    BackEnd::DbRetVal deleteSystemDatabaseChunk(int id);

    inline Chunk*       getSystemDatabaseChunk(int id)
    {
        size_t offset = OS::alignLong(sizeof (DatabaseMetaData)) +
                                              ((size_t)id) * sizeof (Chunk);
        return (Chunk*)(((char*) metaData_) +  offset);
    }

    inline Transaction* getSystemDatabaseTrans(int slot)
    {
       size_t _slot = (size_t)slot;
       size_t offset = OS::alignLong(sizeof (DatabaseMetaData)) +
                       OS::alignLong(MAX_CHUNKS * sizeof (Chunk)) +_slot  * sizeof (Transaction);

       return (Transaction*)(((char*) metaData_) +  offset);
    }

    ThreadInfo* getThreadInfo(int slot);

    bool isLastThread();
    ///Create the catalog tables.
    void createAllCatalogTables();
    ///Create the system tables.
    void createSystemTables();
    ///Create the meta datatable
    void createMetaDataTables();

    void* allocLockHashBuckets();

    Bucket* getLockHashBuckets();

    inline void incrementChunk() { (metaData_->noOfChunks_)++;}
    inline void decrementChunk() { (metaData_->noOfChunks_)--;}

    inline ulong getUniqueIDForChunk()
    {
      return ((metaData_->chunkUniqueID_).getID());
    }

    inline const char* getName(){
        return metaData_->dbName_;
    }
    inline int  getDatabaseID()
    {
        return metaData_->dbID_;
    }
    inline ulong getMaxSize()
    {
         return metaData_->maxSize_;
    }
    inline ulong getCurrentSize()
    {
        return metaData_->curSize_;
    }

    inline uint32_t getNoOfChunks()
    {
      return metaData_->noOfChunks_;
    }

    inline DatabaseMetaData* getMetaDataPtr() { return metaData_; }

    inline Page*  getCurrentPage()
    {
        return metaData_->curPage_;
    }
    inline Page*  getFirstPage()
    {
        return metaData_->firstPage_;
    }
    inline Chunk* getHashIndexChunk()
    {
        return metaData_->hashIndexChunk_;
    }

    inline void setDatabaseID(ulong id)
    {
        metaData_->dbID_ = id;
    }
    inline void setName(const char *name){
        strcpy(metaData_->dbName_ , name);
    }
    inline void setCurrentSize(long size)
    {
         metaData_->curSize_ = size;
    }
    inline void setCurrentPage(Page *page)
    {
        metaData_->curPage_ = page;
    }
    inline void setMaxSize(long size)
    {
        metaData_->maxSize_ = size;
    }
    inline void setNoOfChunks(uint32_t maxChunks)
    {
      metaData_->noOfChunks_ = maxChunks;
    }
    inline void setMetaDataPtr(DatabaseMetaData *ptr) {metaData_ = ptr; }
    inline void setFirstPage(Page *ptr)
    {
        metaData_->firstPage_ = ptr;
    }
    inline void setHashIndexChunk(Chunk* chunk)
    {
        metaData_->hashIndexChunk_ = chunk;
    }
    inline void setUniqueChunkID(ulong id)
    {
      (metaData_->chunkUniqueID_).setID(id);
    }

    // Gets the free page
    // Each page is segmented by PAGE_SIZE, so it checks the pageInfo
    // of each page to determine if the page is free
    Page* getFreePage();
    Page* getFreePage(size_t size);

    void printStatistics();

    int initAllocDatabaseMutex();
    BackEnd::DbRetVal getAllocDatabaseMutex(bool procAccount = true);
    BackEnd::DbRetVal releaseAllocDatabaseMutex(bool procAccount = true);
    BackEnd::DbRetVal getTransTableMutex();
    BackEnd::DbRetVal releaseTransTableMutex();

    int initDatabaseMutex();

    BackEnd::DbRetVal getDatabaseMutex(bool procAccount = true);
    BackEnd::DbRetVal releaseDatabaseMutex(bool procAccount = true);

    int initProcessTableMutex();
    BackEnd::DbRetVal getProcessTableMutex(bool procAccount = true);
    BackEnd::DbRetVal releaseProcessTableMutex(bool procAccount = true);

    BackEnd::DbRetVal recoverMutex(Mutex *mut);

    int  initLockHashBucketTrees(DatabaseManagerImpl& dbmgr);

    static inline int pointer_keys_compare(void*,
                                    const void *key1,const void *key2)
    {
      ulong ulkey1 = (ulong)key1;
      ulong ulkey2 = (ulong)key2;
       return (int)(ulkey1-ulkey2) ;
    }

    int  procSlot;

    inline void setProcSlot(int slot) { procSlot =slot;}
    //checks whether the ptr falls in the range of the database file size
    inline bool isValidAddress(void *addr)
    {
      if ((char*) addr >= ((char*)getMetaDataPtr())  + getMaxSize())
            return false;
        else
            return true;
    }

    friend class DatabaseManagerImpl;
    friend class Table;

};

#endif
